package com.cooperate.fly.service.buildDB.impl;

import com.cooperate.fly.bo.*;
import com.cooperate.fly.controller.BuildSchemaController;
import com.cooperate.fly.mapper.*;
import com.cooperate.fly.service.buildDB.BuildFieldService;
import com.cooperate.fly.web.BaseTreeNode;
import com.cooperate.fly.web.Result;
import com.cooperate.fly.web.TypeTreeNode;
import com.cooperate.fly.web.WebFrontHelper;
import com.google.gson.Gson;
import jxl.common.log.LoggerName;
import net.sf.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by Stellar_Lee on 2016/4/18.
 */
@Service("build_field")
public class BuildFieldServiceImpl implements BuildFieldService {

    @Autowired
    private FieldMapper fieldMapper;

    @Autowired
    private SchemaMapper schemaMapper;

    @Autowired
    private TableMapper tableMapper;

    @Autowired
    private TMatrixXMapper tMatrixXMapper;

    @Autowired
    private TMatrixYMapper tMatrixYMapper;

    @Autowired
    private TMatrixZMapper tMatrixZMapper;

    @Value("#{propertiesReader['create_success']}")
    private String createSuccess;

    @Value("#{propertiesReader['create_fail']}")
    private String createFail;

    @Value("#{propertiesReader['create_same_name_node']}")
    private String createSameNameNode;

    @Value("#{propertiesReader['field_catalog_type']}")
    private int fieldCatalogType;

    @Value("#{propertiesReader['url_split_symbol']}")
    public String urlSplitSymbol;

    @Value("#{propertiesReader['field_root_node_id']}")
    public int fieldRootNodeId;

    @Value("#{propertiesReader['mysql_schema_name']}")
    public String mysqlSchemaName;

    @Resource
    private WebFrontHelper webFrontHelper;

    public static int fieldCount=0;

    private static final Logger logger= LoggerFactory.getLogger(BuildFieldServiceImpl.class);

    @Override
    public Result createFieldNode(Field field) {
        Result result = new Result();
        result.setSuccessful(false);
        result.setMessage(createFail);
        if (!checkFieldName(field)) {
            result.setSuccessful(false);
            result.setMessage(createSameNameNode);
            return result;
        }
        //在节点所属于的schema database中获得最大的order进行+1
        //将数据库中最大节点order标号放在对应数据库的childCount的字段中
        Schema schema=schemaMapper.selectByPrimaryKey(field.getSchemaId());
        if(schema==null){
            return result;
        }
        if(schema.getChildCount()!=null){
            field.setOrder(schema.getChildCount()+1);
            schema.setChildCount(schema.getChildCount()+1);
        }else{
            field.setOrder(1);
            schema.setChildCount(1);
        }
        schemaMapper.updateByPrimaryKey(schema);
        int res = fieldMapper.insert(field);
        Integer fid=field.getId();
        if ((res == 1)&&(fid!=null)) {
            Field parentField=fieldMapper.selectByPrimaryKey(field.getParentId());
            String path=null;
            if(parentField!=null){
                path=parentField.getPath();
                path+="/"+fid;
            }else{
                path="/"+fid;
            }
            fieldMapper.updatePathById(path, fid);
            result.setMessage(createSuccess);
            result.setData(field.getId());
            result.setSuccessful(true);
        }
        return result;
    }

    @Override
    public Result create3Matrix(int fid,String xName,String xTitle,String yName,String yTitle,String zName){
        TMatrixX tMatrixX=new TMatrixX();
        tMatrixX.setFid(fid);
        tMatrixX.setName(xName);
        tMatrixX.setValue(xTitle);
        TMatrixY tMatrixY=new TMatrixY();
        tMatrixY.setFid(fid);
        tMatrixY.setName(yName);
        tMatrixY.setValue(yTitle);
        TMatrixZWithBLOBs tMatrixZ=new TMatrixZWithBLOBs();
        tMatrixZ.setFid(fid);
        tMatrixZ.setName(zName);
        int res=0;
        res+=tMatrixXMapper.insert(tMatrixX);
        res+=tMatrixYMapper.insert(tMatrixY);
        res+=tMatrixZMapper.insert(tMatrixZ);
        Result result=new Result();
        if(res==3){
            result.setSuccessful(true);
        }else {
            result.setSuccessful(false);
        }
        return result;
    }
    @Override
    public Result get3MatrixInfo(int id){
        Result result=new Result();
        return result;
    }
    @Override
    public Result deleteFieldNode(Field field) {
        return deleteFieldNodeById(field.getId());
    }

    @Override
    public Result deleteFieldNodeById(int id) {
        Result result = new Result();
        ArrayList<Field> sumFields = new ArrayList<Field>();
        //delete from the root node
        if (id == fieldRootNodeId) {
            ArrayList<Field> rootFileds = new ArrayList<Field>();
            rootFileds = fieldMapper.selectByParentId(id);
            for (Field field2 : rootFileds)
                getSonFields(field2, sumFields);
            sumFields.addAll(rootFileds);
        } else {
            Field field = fieldMapper.selectByPrimaryKey(id);
            getSonFields(field, sumFields);
            sumFields.add(field);
        }
        int count = 0;
        for (Field field1 : sumFields) {
            int res = fieldMapper.deleteByPrimaryKey(field1.getId());
            //int tableNum=tableMapper.existTable("table_"+field1.getSchemaId());
            //int tableNum2=tableMapper.checkTableExist("table_"+field1.getSchemaId());
            //if(tableNum==1) {
                //if(tableMapper.selectByFieldId("table_"+field1.getSchemaId(),field1.getId())>0) {
            try{
                tableMapper.deleteByFieldId("table_" + field1.getSchemaId(), field1.getId());
            }catch (Exception e){

            }
                //}
            //}
            if (res == 1) {
                count++;
            }
        }
        if (count == sumFields.size())
            result.setMessage(createSuccess);
        else {
            result.setMessage(createFail);
            result.setSuccessful(false);
        }
        return result;
    }

    @Override
    public Result updateFieldNode(Field field) {
        Result result = new Result();
        Field sourceField = fieldMapper.selectByPrimaryKey(field.getId());
        sourceField.setName(field.getName());
        sourceField.setRemark3(field.getRemark3());
        sourceField.setRemark1(field.getRemark1());
        if (!checkFieldName(sourceField)) {
            result.setSuccessful(false);
            result.setMessage(createSameNameNode);
            return result;
        }
        if (sourceField != null) {
            fieldMapper.updateByPrimaryKeyWithBLOBs(sourceField);
            result.setMessage(createSuccess);
        } else {
            result.setMessage(createFail);
            result.setSuccessful(false);
        }
        return result;
    }

    @Override
    public Result searchFieldNode(Field field) {
        return null;
    }

    @Override
    public Result searchFieldNodeById(int id) {
        return null;
    }

    @Override
    public String loadField(int databaseId) {
        ArrayList<Field> fields = fieldMapper.selectByDId(databaseId);
        Schema schema = schemaMapper.selectByPrimaryKey(databaseId);
        return webFrontHelper.buildFieldTree(fields, schema.getName());
    }

    @Override
    public Map<String, String> loadPartFile(int fieldId) {
        Field field = fieldMapper.selectByPrimaryKey(fieldId);
        ArrayList<Field> sumFields = new ArrayList<Field>();
        ArrayList<Schema> sumSchemas = new ArrayList<Schema>();
        Map<String, String> maps = new HashMap<String, String>();
        if (field != null) {
            getParentFields(field, sumFields);
            getSonFields(field, sumFields);
            sumFields.add(field);
            Schema schema = schemaMapper.selectByPrimaryKey(field.getSchemaId());
            getParentSchema(field, schema, sumSchemas);
            maps.put("fieldStr", webFrontHelper.buildFieldTree(sumFields, schema.getName()));
            maps.put("schemaStr", webFrontHelper.buildSchemaTree(sumSchemas));
            return maps;
        } else
            return null;
    }

    public Map<String, String> loadPartField(int fieldId) {
        Field field = fieldMapper.selectByPrimaryKey(fieldId);
        ArrayList<Field> sumFields = new ArrayList<Field>();
        ArrayList<Schema> sumSchemas = new ArrayList<Schema>();
        Map<String, String> maps = new HashMap<String, String>();
        if (field != null) {
            getParentFields(field, sumFields);
            getSonFields(field, sumFields);
            sumFields.add(field);
            Schema schema = schemaMapper.selectByPrimaryKey(field.getSchemaId());
            getParentSchema(field, schema, sumSchemas);
            maps.put("fieldStr", new Gson().toJson(sumFields));
            maps.put("schemaStr", new Gson().toJson(sumSchemas));
            return maps;
        } else
            return null;
    }

    @Override
    public String parseFieldUrl(String schemaPath,String fieldPath) {
        String[] schemaNames=schemaPath.split(urlSplitSymbol);
        int parentId=0;
        Schema schema=null;
        for(int i=0;i<schemaNames.length;i++){
            String name=schemaNames[i];
            schema=schemaMapper.selectByNameAndParentId(name,parentId);
            parentId=schema.getId();
        }
        if(schema!=null) {
            String[] fieldNames = fieldPath.split(urlSplitSymbol);
            parentId = fieldRootNodeId;
            Field field = null;
            Map<String, String> maps=null;
            for (int i = 0; i < fieldNames.length; i++) {
                String name = fieldNames[i];
                field = fieldMapper.selectByNameAndParentIdAndSchemaId(name, parentId, schema.getId());
                parentId = field.getId();
            }
            if (field != null) {
                maps=loadPartField(field.getId());
            }
            if (maps !=null) {
                return maps.get("fieldStr")+"\t"+maps.get("schemaStr");
            }
        }
        return null;
    }

    @Override
    public Result getRemark1(int id) {
        Result result = new Result();
        Field field = fieldMapper.selectByPrimaryKey(id);
        if (field != null) {
            result.setData(field.getRemark1());
        } else {
            result.setSuccessful(false);
        }
        return result;
    }

    @Override
    public Result getRemark3(int id) {
        Result result = new Result();
        Field field = fieldMapper.selectByPrimaryKey(id);
        if (field != null) {
            result.setData(field.getRemark3());
        } else {
            result.setSuccessful(false);
        }
        return result;
    }

    //递归循环该节点的子节点直到叶子节点的schema节点,不包含查询节点本身
    public void getSonFields(Field field, ArrayList<Field> fields) {
        if (field.getType() == fieldCatalogType) {
            ArrayList<Field> fieldList = fieldMapper.selectByParentId(field.getId());
            if(fieldList!=null) {
                fields.addAll(fieldList);
                for (Field field1 : fieldList) {
                    getSonFields(field1, fields);
                }
            }
        }
    }

    public void getParentFields(Field field, ArrayList<Field> fields) {
        if (field.getParentId() != fieldRootNodeId) {
            Field parentField = fieldMapper.selectByPrimaryKey(field.getId());
            fields.add(parentField);
        }
    }

    public void getParentSchema(Field field, Schema schema, ArrayList<Schema> schemas) {
        if (schema.getParentId() != 0) {
            Schema parentSchema = schemaMapper.selectByPrimaryKey(schema.getParentId());
            schemas.add(parentSchema);
        }
    }

    @Override
    public boolean checkFieldName(Field field) {
        if(field.getName()==null){
            return false;
        }
        ArrayList<Field> fields = fieldMapper.selectByParentId(field.getParentId());
        logger.debug("field:id="+field.getId()+" name="+field.getName()+" schemaId="+field.getSchemaId());
        if (fields != null) {
            //update field
            if(field.getId()!=null){
                for (Field f : fields) {
                    if(f.getName()==null&&field.getName()!=null||f.getSchemaId()==null&&field.getSchemaId()!=null){
                        continue;
                    }
                    if (f.getName().equals(field.getName())&&!(f.getId().equals(field.getId()))&&
                            f.getSchemaId().equals(field.getSchemaId()))
                        return false;
                }
            }else{
                for (Field f : fields) {
                    logger.debug("f:id="+f.getId()+" name="+f.getName()+" schemaId="+f.getSchemaId());
                    if(f.getName()==null&&field.getName()!=null||f.getSchemaId()==null&&field.getSchemaId()!=null) {
                        continue;
                    }
                    if (f.getName().equals(field.getName())&&f.getSchemaId().equals(field.getSchemaId()))
                        return false;
                }
            }
        }
        return true;
    }

    //download the data of the database
    @Override
    public File downloadFields(int databaseId,int fieldId) throws Exception {
        ArrayList<Field> fields =new ArrayList<Field>();
        if(fieldId==fieldRootNodeId){
             fields=fieldMapper.selectByDId(databaseId);
        }else {
            Field temp=fieldMapper.selectByPrimaryKey(fieldId);
            getSonFields(temp,fields);
        }
        String data;
        if (fields != null) {
            data = new Gson().toJson(fields);
        } else {
            data = "[]";
        }
        String originPath = this.getClass().getResource("/").getPath();
        originPath = originPath.substring(0, originPath.indexOf("WEB-INF"));
        originPath = originPath + "download";
        String fileName = "database_" + databaseId + "_";
        String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        fileName = fileName + time + ".json";
        File originFile = new File(originPath);
        if (!originFile.exists()) {
            originFile.mkdirs();
        }
        String filePath = originPath + File.separator + fileName;
        File sourceFile = new File(filePath);
        if (!sourceFile.exists()) {
            sourceFile.createNewFile();
        }
        FileOutputStream os = new FileOutputStream(filePath);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
        writer.write(data);
        writer.close();
        os.close();
        return sourceFile;
    }

    @Override
    @Transactional
    public Result freshData(String fieldsStr,int schemaId,int fieldId) {
        JSONArray jsonArray = JSONArray.fromObject(fieldsStr);
        Result result = new Result();
        result.setSuccessful(false);
        result.setMessage(createFail);
        result.setData(500);
        List<Field> list = JSONArray.toList(jsonArray, Field.class);
        int count = 0;
        if(fieldId==fieldRootNodeId) {
            fieldMapper.deleteBySchemaId(schemaId);//if runtime exception happened, transaction roll back
        }else{
            ArrayList<Field> deleteFields=new ArrayList<Field>();
            Field field = fieldMapper.selectByPrimaryKey(fieldId);
            getSonFields(field, deleteFields);
            if(deleteFields.size()>0) {
                for (Field field1 : deleteFields) {
                    fieldMapper.deleteByPrimaryKey(field1.getId());
                }
            }
        }
        count=reStructFields(list,schemaId,fieldId);
        if (count == list.size()) {
            result.setMessage(createSuccess);
            result.setSuccessful(true);
            result.setData(200);
            return result;
        } else {
            int i = 1 / 0;// make the runtime exception
        }
        return result;
    }

    private int  reStructFields(List<Field> sourceFields,int sid,int fid){
        HashMap<Integer,ArrayList<Field>> parentIdToSonIds=new HashMap<Integer, ArrayList<Field>>();
        HashMap<Integer,Field> idToField=new HashMap<Integer, Field>();
        for(Field field:sourceFields){
            idToField.put(field.getId(),field);
            int pid=field.getParentId();
            if(parentIdToSonIds.get(pid)!=null){
                parentIdToSonIds.get(pid).add(field);
            }else{
                ArrayList<Field> sonFields=new ArrayList<Field>();
                sonFields.add(field);
                parentIdToSonIds.put(pid,sonFields);
            }
        }
        //获得根节点的第一批子节点,即父节点的id不属于这个集合中。
        List<Field> sons=new ArrayList<Field>();
        for(Field field:sourceFields){
            if(idToField.get(field.getParentId())==null){
                sons.add(field);
            }
        }

        int newPid=fid;
        int count=0;
        //map key: Array0 oldPid, array1 newPid, List<Field> sons
        Queue<StructNode> queue=new LinkedList<StructNode>();
        if(sons.size()>0){
            StructNode node=new StructNode();
            node.setNewPid(newPid);
            node.setSons(sons);
            queue.add(node);
        }

        while (!queue.isEmpty()){
            StructNode q=queue.remove();
            newPid=q.getNewPid();
            sons=q.getSons();
            for(Field field:sons){
                int tempOid=field.getId();
                field.setSchemaId(sid);
                field.setParentId(newPid);
                field.setId(null);
                count+=fieldMapper.insert(field);
                int tempNid=field.getId();
                StructNode tempNode=new StructNode();
                tempNode.setNewPid(tempNid);
                if(parentIdToSonIds.get(tempOid)!=null){
                    tempNode.setSons(parentIdToSonIds.get(tempOid));
                    queue.add(tempNode);
                }
            }
        }
        //fieldRecursion(parentIdToSonIds,0,0,sid);
        return count;
    }

    private void fieldRecursion(HashMap<Integer,ArrayList<Field>> fieldMap,int oldPid,int newPid,int sid){
        ArrayList<Field> sons=fieldMap.get(oldPid);
        if(sons!=null){
            for(Field field:sons){
                field.setSchemaId(sid);
                oldPid=field.getId();
                field.setId(null);
                field.setParentId(newPid);
                fieldMapper.insert(field);
                int tempId=field.getId();
                fieldRecursion(fieldMap,oldPid,tempId,sid);
            }
        }else
            return;
    }

    class StructNode{
        int newPid;

        public int getNewPid() {
            return newPid;
        }

        public void setNewPid(int newPid) {
            this.newPid = newPid;
        }


        public List<Field> getSons() {
            return sons;
        }

        public void setSons(List<Field> sons) {
            this.sons = sons;
        }

        List<Field> sons;
    }

    @Override
    public Result exchangeNode(int frontNodeId,int backNodeId){
        Result result=new Result();
        result.setSuccessful(false);
        result.setMessage(createFail);
        Field frontField=fieldMapper.selectByPrimaryKey(frontNodeId);
        Field backField=fieldMapper.selectByPrimaryKey(backNodeId);
        if(frontField!=null&&backField!=null && (frontField.getParentId().equals(backField.getParentId()))){
            if(frontField.getOrder()!=null&&backField.getOrder()!=null) {
                int tempOrder = frontField.getOrder();
                frontField.setOrder(backField.getOrder());
                backField.setOrder(tempOrder);
                fieldMapper.updateByPrimaryKey(frontField);
                fieldMapper.updateByPrimaryKey(backField);
                result.setSuccessful(true);
                result.setMessage(createSuccess);
            }
        }
        return result;
    }
}
